package parsers;

import common.Initializer;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import localization.Localization;
import localization.LocalizationResource;
import org.jdom.*;
import org.jdom.input.DOMBuilder;
import org.xml.sax.SAXException;
import templates.ExtensionTemplate;
import templates.PresentationTemplate;
import templates.PropertyTemplate;
import templates.Template;
import utils.GenericPair;

public class XmlParser implements Initializer {

    private Initializer.ObjectType objType;
    private String path;
    private short uid = 0x0000;
    private static final String XML_TITLE = "title";
    private static final String XML_ENTITY_TEMPLATES = "entity-templates";
    private static final String XML_ENTITY_TEMPLATE = "entity-template";
    private static final String XML_LOCALIZATION_FILES = "localization-files";
    private static final String XML_LOCALIZATION_FILE = "localization-file";
    private static final String XML_HELP_FILE = "help-file";
    private static final String XML_DATABASE = "database-connection";
    private static final String XML_STRING = "string";
    private static final String XML_ATTR_FILENAME = "filename";
    private static final String XML_ATTR_ALIAS = "alias";
    private static final String XML_ATTR_TABLENAME = "table-name";
    private static final String XML_NAME = "name";
    private static final String XML_PROPERTIES = "properties";
    private static final String XML_PROPERTY = "property";
    private static final String XML_ATTR_TYPE = "type";
    private static final String XML_ATTR_NULLVALUE = "null-value";
    private static final String XML_PRESENTATIONS = "presentations";
    private static final String XML_PRESENTATION = "presentation";
    private static final String XML_EXTERNALS = "externals";
    private static final String XML_EXTERNAL = "external";
    private static final String XML_ATTR_MODE = "mode";
    private static final String XML_PARAM = "param";
    private static final String XML_VALUE = "value";
    private static final String XML_FOREIGNKEY = "foreign-key";
    private static final String XML_PRIMARYKEY = "primary-key";
    private static final String XML_EXTENTIONS = "extensions";
    private static final String XML_EXTENTION = "extension";
    private static final String XML_PANEL = "panel";
    private static final String XML_ATTR_STRATEGY = "strategy";
    private static final String XML_LANGUAGE = "language";

    public XmlParser(String appDescriptor) {
        path = appDescriptor;
    }

    @Override
    public Object init(Object obj) {
        if (objType == Initializer.ObjectType.Application) {
            try {
                return parseApplication(path);
            } catch (IncorrectKeysException ex) {
                Logger.getLogger(XmlParser.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else if (objType == Initializer.ObjectType.Template) {
            try {
                return parseTemplate(path);
            } catch (IncorrectKeysException ex) {
                Logger.getLogger(XmlParser.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else if (objType == Initializer.ObjectType.Localization) {
            return parseLocalization(path);
        }
        return null;
    }

    @Override
    public void setManagedObject(Initializer.ObjectType obj) {
        objType = obj;
    }

    public Object parseApplication(String resource) throws IncorrectKeysException {
        Map<String, Object> map = new TreeMap<String, Object>();
        Collection<Template> templateCollection = new LinkedList<Template>();
        Collection<LocalizationResource> locCollection = new LinkedList<LocalizationResource>();
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DOMBuilder builder2 = new DOMBuilder();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder2.build(builder.parse(resource));
            Element root = document.getRootElement();
            List children = root.getChildren();
            for (int i = 0; i < children.size(); i++) {
                Element e = (Element) children.get(i);
                if (e.getName().equals(XML_TITLE)) {
                    map.put("title", e.getText());
                } else if (e.getName().equals(XML_DATABASE)) {
                    map.put("connectionString", e.getAttributeValue(XML_STRING));
                } else if (e.getName().equals(XML_ENTITY_TEMPLATES)) {
                    List templates = e.getChildren();

                    for (int j = 0; j < templates.size(); j++) {
                        Element e2 = (Element) templates.get(j);
                        if (e2.getName().equals(XML_ENTITY_TEMPLATE)) {
                            setManagedObject(Initializer.ObjectType.Template);
                            uid = (short) ((uid & 0xFF00) + 0x0100);
                            Template t = (Template) parseTemplate(e2.getAttributeValue(XML_ATTR_FILENAME));
                            templateCollection.add(t);
                        }
                    }
                    map.put("templates", templateCollection);
                } else if (e.getName().equals(XML_LOCALIZATION_FILES)) {
                    List templates = e.getChildren();

                    for (int j = 0; j < templates.size(); j++) {
                        Element e2 = (Element) templates.get(j);
                        if (e2.getName().equals(XML_LOCALIZATION_FILE)) {
                            setManagedObject(Initializer.ObjectType.Localization);
                            uid = (short) ((uid & 0xFF00) + 0x0100);
                            LocalizationResource l = (LocalizationResource) parseLocalization(e2.getAttributeValue(XML_ATTR_FILENAME));
                            locCollection.add(l);
                        }
                    }
                    map.put("templates", templateCollection);
                    map.put("localizations", locCollection);
                }
            }
        } catch (ParserConfigurationException e) {
            throw new RuntimeException();
        } catch (IOException e) {
            throw new RuntimeException();
        } catch (SAXException e) {
            throw new RuntimeException();
        }
        return map;
    }

    public Object parseTemplate(String resource) throws IncorrectKeysException {
        Collection<PropertyTemplate> propertyTemplates = new LinkedList<PropertyTemplate>();
        Collection<PresentationTemplate> presentationTemplates = new LinkedList<PresentationTemplate>();
        Collection<ExtensionTemplate> extensionTemplates = new LinkedList<ExtensionTemplate>();
        Map<String, Object> templateMap = new TreeMap<String, Object>();
        Template newTemplate;
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DOMBuilder builder2 = new DOMBuilder();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder2.build(builder.parse(resource));
            Element root = document.getRootElement();
            templateMap.put(XML_ATTR_ALIAS, root.getAttributeValue(XML_ATTR_ALIAS));
            templateMap.put(XML_ATTR_TABLENAME, root.getAttributeValue(XML_ATTR_TABLENAME));

            List children = root.getChildren();
            for (int i = 0; i < children.size(); i++) {
                Element e = (Element) children.get(i);
                if (e.getName().equals(XML_NAME)) {
                    templateMap.put(XML_NAME, e.getText());

                    templateMap.put("tableUid", uid);
                } else if (e.getName().equals(XML_PROPERTIES)) {
                    List properties = e.getChildren();
                    for (int j = 0; j < properties.size(); j++) {
                        Element e2 = (Element) properties.get(j);
                        if (e2.getName().equals(XML_PROPERTY)) {

                            Collection<GenericPair<String, String>> newParamsColl = new LinkedList<GenericPair<String, String>>();
                            List params = e2.getChildren();
                            //----------------
                            byte attr = 0;
                            String nullValue = "";
                            String externalTable = "";
                            int correct = 0;
                            for (int k = 0; k < params.size(); k++) {
                                Element e3 = (Element) params.get(k);
                                if (e3.getName().equals(XML_PRIMARYKEY)) {
                                    if (correct == 0) {
                                        attr += 0x01;
                                        correct = 1;
                                    }
                                }
                                if (e3.getName().equals(XML_FOREIGNKEY)) {
                                    if (correct != 1) {
                                        attr += 0x02;
                                        externalTable = e3.getText();
                                        correct = 2;
                                    } else {
                                        throw new IncorrectKeysException("Property can't be foreign and primary keys");
                                    }
                                }
                                if (e3.getName().equals(XML_ATTR_NULLVALUE)) {
                                    if (correct != 1) {
                                        attr += 0x04;
                                        nullValue = e3.getText();
                                        correct = 3;
                                    } else {
                                        throw new IncorrectKeysException("Property can't be primary key and have nullValue");
                                    }
                                }
                                if (e3.getName().equals(XML_PARAM)) {
                                    newParamsColl.add(new GenericPair<String, String>(e3.getAttributeValue(XML_NAME), e3.getAttributeValue(XML_VALUE)));
                                }
                            }
                            uid += 0x0001;
                            attr = (byte) ~attr;
                            propertyTemplates.add(new PropertyTemplate(uid, e2.getAttributeValue(XML_NAME), e2.getAttributeValue(XML_ATTR_TYPE), nullValue, externalTable, attr, newParamsColl));
                        }
                    }
                } else if (e.getName().equals(XML_PRESENTATIONS)) {
                    short uid2 = (short) (0xFF00 & uid);
                    List presentaions = e.getChildren();
                    for (int j = 0; j < presentaions.size(); j++) {
                        Element e2 = (Element) presentaions.get(j);
                        if (e2.getName().equals(XML_PRESENTATION)) {
                            Collection<GenericPair<String, Boolean>> newExternalsColl = new LinkedList<GenericPair<String, Boolean>>();
                            List parametrs = e2.getChildren();
                            Map<String, Object> paramCol = new TreeMap<String, Object>();
                            for (int k = 0; k < parametrs.size(); k++) {
                                Element e3 = (Element) parametrs.get(k);
                                if (e3.getName().equals(XML_NAME)) {
                                    paramCol.put(XML_NAME, e3.getText());
                                } else if (e3.getName().equals(XML_ATTR_NULLVALUE)) {
                                    paramCol.put(XML_ATTR_NULLVALUE, e3.getText());
                                } else if (e3.getName().equals(XML_EXTERNALS)) {
                                    List externals = e3.getChildren();
                                    for (int q = 0; q < externals.size(); q++) {
                                        Element e4 = (Element) externals.get(q);
                                        if (e4.getName().equals(XML_EXTERNAL)) {
                                            String mode = e4.getAttributeValue(XML_ATTR_MODE);
                                            newExternalsColl.add(new GenericPair<String, Boolean>(e4.getAttributeValue("presentation"), (mode == null) ? false : mode.equals("replacing")));
                                        }
                                    }
                                }
                            }
                            uid += 0x0001;
                            uid2 += 0x0001;
                            presentationTemplates.add(new PresentationTemplate(uid, uid2, e2.getAttributeValue(XML_ATTR_ALIAS), (String) paramCol.get(XML_NAME), e2.getAttributeValue(XML_ATTR_TYPE), (String) paramCol.get(XML_ATTR_NULLVALUE), newExternalsColl));
                        }
                    }
                } else if (e.getName().equals(XML_EXTENTIONS)) {
                    List extensions = e.getChildren();
                    for (int j = 0; j < extensions.size(); j++) {
                        Element e2 = (Element) extensions.get(j);
                        if (e2.getName().equals(XML_EXTENTION)) {
                            List parametrs = e2.getChildren();
                            Map<String, Object> paramCol = new TreeMap<String, Object>();
                            for (int k = 0; k < parametrs.size(); k++) {
                                Element e3 = (Element) parametrs.get(k);
                                if (e3.getName().equals(XML_NAME)) {
                                    paramCol.put(XML_NAME, e3.getText());
                                } else if (e3.getName().equals(XML_PANEL)) {
                                    CDATA ctext = new CDATA(e3.getText());
                                    paramCol.put(XML_PANEL, ctext);
                                }
                            }
                            extensionTemplates.add(new ExtensionTemplate((String) paramCol.get(XML_NAME), e2.getAttributeValue(XML_ATTR_STRATEGY), (CDATA) paramCol.get(XML_PANEL)));
                        }
                    }
                }
            }
            newTemplate = new Template((Short) templateMap.get("tableUid"), (String) templateMap.get(XML_NAME), root.getAttributeValue(XML_ATTR_ALIAS), root.getAttributeValue(XML_ATTR_TABLENAME), propertyTemplates, presentationTemplates, extensionTemplates);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException();
        } catch (IOException e) {
            throw new RuntimeException();
        } catch (SAXException e) {
            throw new RuntimeException();
        }
        return newTemplate;
    }

    public Object parseLocalization(String resource) {
        Map<String, Object> localizationMap = new TreeMap<String, Object>();
        LocalizationResource newLocalizationRes = null;
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DOMBuilder builder2 = new DOMBuilder();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder2.build(builder.parse(resource));
            Element root = document.getRootElement();
            List children = root.getChildren();
            /*for (int i = 0; i < children.size(); i++) {
            Element e = (Element) children.get(i);
            if (e.getName().equals(XML_LOCALIZATION)) {
            List properties = e.getChildren();*/
            Map<String, String> map = new TreeMap<String, String>();
            for (int j = 0; j < children.size(); j++) {
                Element e2 = (Element) children.get(j);
                if (e2.getName().equals(XML_LANGUAGE)) {
                    localizationMap.put(XML_LANGUAGE, e2.getText());
                } else if (e2.getName().equals(XML_STRING)) {
                    map.put(e2.getAttributeValue(XML_NAME), e2.getText());
                }else if (e2.getName().equals(XML_HELP_FILE)){
                    localizationMap.put(XML_HELP_FILE, e2.getText());
                }

            }
            newLocalizationRes = new LocalizationResource(uid, (String) localizationMap.get(XML_LANGUAGE), map, (String) localizationMap.get(XML_HELP_FILE));
            //}
            //}

        } catch (ParserConfigurationException ex) {
            Logger.getLogger(XmlParser.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SAXException ex) {
            Logger.getLogger(XmlParser.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(XmlParser.class.getName()).log(Level.SEVERE, null, ex);
        }
        return newLocalizationRes;
    }
}
